package xman.mssh.hibernate.service;

import java.beans.Introspector;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@Repository
public class RepositoryService {
    
    protected Logger logger = LoggerFactory.getLogger(RepositoryService.class);
    
    @PersistenceContext(unitName = "mysql")
    protected EntityManager em;
    
    /*-
     * createQuery(hql)
     * createNativeQuery(sql)
     */
    
    /*
     * persist或save，会将一个持久化实体状态由瞬时态转化为持久态（TO -> PO），并且将该对象缓存在session当中
     */
    public void persisit(Object obj) {
        em.persist(obj);
    }
    
    /*
     * merge方法返回了一个持久化对象，但原对象还是保持为瞬时态(TO -> TO)
     */
    public Object merge(Object obj) {
        return em.merge(obj);
    }
    
    public void remove(Object obj) {
        em.remove(obj);
    }
    
    public void clear() {
        em.clear();
    }
    
    public void flush() {
        em.flush();
    }
    
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> T get(Class<T> entityClass, Serializable entityId) {
        if (entityId == null)
            throw new RuntimeException(entityClass.getName() + ":param is error");
        return em.find(entityClass, entityId);
    }
    
    @SuppressWarnings("unchecked")
    public <T> T get1st(Class<T> entityClass, String wherejpql, Object[] queryParams,
            LinkedHashMap<String, OrderbyEnumType> orderby) {
        Query query = createQuery(entityClass, wherejpql, queryParams, orderby);
        List<T> ret = (List<T>) query.getResultList();
        return (ret == null || ret.size() == 0) ? null : ret.get(0);
    }
    
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> List<T> list(Class<T> entityClass, String wherejpql, Object[] queryParams,
            LinkedHashMap<String, OrderbyEnumType> orderby) {
        Query query = createQuery(entityClass, wherejpql, queryParams, orderby);
        return (List<T>) query.getResultList();
    }
    
    public <T> void delete(Class<T> entityClass, Object entityId) {
        em.remove(em.getReference(entityClass, entityId));
    }
    
    public <T> void delete(Class<T> entityClass, Serializable... entityIds) {
        for (Object id : entityIds) {
            em.remove(em.getReference(entityClass, id));
        }
    }
    
    protected <T> Query createQuery(Class<T> entityClass, String wherejpql, Object[] queryParams,
            LinkedHashMap<String, OrderbyEnumType> orderby) {
        Query query = em.createQuery(" from " + getEntityName(entityClass) + " "
                + (StringUtils.isEmpty(StringUtils.trim(wherejpql)) ? "" : " where " + wherejpql));
        setQueryParams(query, queryParams);
        return query;
    }
    
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> QueryResult<T> scroll(Class<T> entityClass, Integer firstindex, Integer maxresult, String wherejpql,
            Object[] queryParams, LinkedHashMap<String, OrderbyEnumType> orderby) throws Exception {
        QueryResult<T> qr = new QueryResult<T>();
        Query query = createQuery(entityClass, wherejpql, queryParams, orderby);
        if (firstindex == null || firstindex < 0)
            throw new Exception("firstindex error");
        if (maxresult == null || maxresult <= 0)
            throw new Exception("maxresutl error");
        query.setFirstResult(firstindex).setMaxResults(maxresult);
        qr.setResultlist(query.getResultList());
        
        qr.setTotalrecord(count(entityClass, wherejpql, queryParams));
        return qr;
    }
    
    protected <T> Long count(Class<T> entityClass, String wherejpql, Object[] queryParams) {
        Query query = em.createQuery("select count(1) from " + getEntityName(entityClass) + "  "
                + (StringUtils.isEmpty(StringUtils.trim(wherejpql)) ? "" : " where " + wherejpql));
        setQueryParams(query, queryParams);
        return ((Long) query.getSingleResult());
    }
    
    protected static <E> String getEntityName(Class<E> clazz) {
        // 获取简单类名
        String entityname = clazz.getSimpleName();
        // 从类字节码上获取类上的@Entity注解
        Entity entity = clazz.getAnnotation(Entity.class);
        if (StringUtils.isNotEmpty(StringUtils.trim(entity.name()))) {
            // 从@Entity注解上获取name属性的值
            entityname = entity.name();
        }
        return entityname;
    }
    
    // 从JPA环境中获取数据源对象
    public Connection getJPAConnection() {
        return em.unwrap(java.sql.Connection.class);
    }
    
    public boolean multiTransactionJDBCHandler(LinkedList<String> sqls) {
        Connection conn = getJPAConnection();
        try {
            conn.setAutoCommit(false);
            Statement stmt = conn.createStatement();
            for (String sql : sqls) {
                stmt.addBatch(sql);
            }
            stmt.executeBatch();
            conn.commit();
        } catch (Exception ex) {
            ex.printStackTrace();
            try {
                conn.rollback();
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return false;
        } finally {
            try {
                if (null != conn) {
                    conn.setAutoCommit(true);
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return true;
    }
    
    public boolean multiTransactionJTSHandler(LinkedHashMap<Object, MultiTransactionEventType> handlers) {
        try {
            for (Object entity : handlers.keySet()) {
                if (entity == null) {
                    continue;
                }
                MultiTransactionEventType type = handlers.get(entity);
                switch (type) {
                    case MULTI_EVENT_PERSIST:
                        em.persist(entity);
                        break;
                    case MULTI_EVENT_MERGE:
                        em.merge(entity);
                        break;
                    case MULTI_EVENT_REMOVE:
                        em.remove(em.merge(entity));
                        break;
                    default:
                        break;
                }
            }
            return true;
        } catch (RuntimeException e) {
            throw new RuntimeException();
        }
    }
    
    protected static void setQueryParams(Query query, Object[] queryParams) {
        if (queryParams != null && queryParams.length > 0) {
            for (int i = 0; i < queryParams.length; i++) {
                query.setParameter(i + 1, queryParams[i]);
            }
        }
    }
    
    protected static <T> String buildOrderby(Class<T> entityClass, LinkedHashMap<String, OrderbyEnumType> orderby) {
        StringBuffer orderbysql = new StringBuffer("");
        if (orderby != null && orderby.size() > 0) {
            orderbysql.append(" order by ");
            boolean is1st = true;
            for (String key : orderby.keySet()) {
                if (is1st) {
                    is1st = false;
                } else {
                    orderbysql.append(" , ");
                }
                orderbysql.append(key).append(" ").append(orderby.get(key).getOrderbyTypeString());
            }
            return orderbysql.toString();
        } else {
            return defaultOrderby(entityClass);
        }
    }
    
    protected static <T> String defaultOrderby(Class<T> entityClass) {
        String fieldName = null;
        StringBuffer orderbyql = new StringBuffer("");
        Field[] fields = entityClass.getDeclaredFields();
        for (Field f : fields) { // field
            if (f.isAnnotationPresent(Id.class)) {
                fieldName = f.getName();
                break;
            }
        }
        if (StringUtils.isEmpty(fieldName)) {// method
            Method[] methods = entityClass.getMethods();
            for (Method method : methods) {
                // 判断方法上面是否有@Id注解，如果有是表明是实体类主键属性所对应的get方法
                if (method.isAnnotationPresent(Id.class)) {
                    // 获取方法所对应的成员属性名
                    fieldName = Introspector.decapitalize(method.getName().substring(3));
                    break;
                }
            }
        }
        
        if (StringUtils.isNotEmpty(fieldName)) {
            orderbyql.append(" order by " + fieldName + " desc");
        }
        return orderbyql.toString();
    }
}
